package CalendarWS;

import java.net.URI;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.DELETE;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;
import javax.servlet.http.HttpServletResponse;

import com.google.gdata.client.GoogleAuthTokenFactory.UserToken;
import com.google.gdata.client.authn.oauth.OAuthException;
import com.google.gdata.client.calendar.*;
import com.google.gdata.data.*;
import com.google.gdata.data.calendar.*;
import com.google.gdata.data.extensions.*;
import com.google.gdata.util.*;
import com.google.gdata.util.common.xml.XmlWriter;

import java.lang.String;
import java.net.*;
import java.io.*;
import java.sql.*;
import java.util.*;

import sample.util.*;
//package com.google.code.samples.apps.marketplace;

import com.google.gdata.data.DateTime; // google's datetime may be JodaTime
import com.google.gdata.data.extensions.*;
import com.google.gdata.data.calendar.*;
import com.google.gson.Gson;
/**
 * @author mike
 * Performs CRUD operations via HTTP (POST, GET, PUT, DELETE http methods)
 */


/**
 * @author mike
 * Annotated class for use with Jersey api
 */
//Sets the path to base URL + /@Path
@Path("/events")
public class EventCRUD {
	private PreparedStatement _ps; 
	private Connection _conn; // database connection
	private Gson _gson; // json marshal/unmarshal
	
	//  TODO: find better way to keep session state
	private HashMap<String, CalUpdater> _users; // stores authenticated users
	
	public EventCRUD() {
        try{ // connection to db
        	String _sqlUrl = "jdbc:mysql://localhost/smotest";
        	String uName = "root";
        	String uPass = "lepton";
        	Class.forName("com.mysql.jdbc.Driver").newInstance();
        	_conn = DriverManager.getConnection(_sqlUrl, uName, uPass);
        	
        } catch(Exception e) {
        	System.out.println("db connection error...");
        	e.printStackTrace();
        }
        
        _gson = new Gson();
	} // end constructor
	
	/**
	 * Add a new event to app from json
	 * @return nothing; instead: http redirect
	 * @throws SQLException 
	 */
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	public void postEventXML(String eventJson) throws SQLException {
		EventObject event = _gson.fromJson(eventJson, EventObject.class); // missing fields
		
		DateTime start = DateTime.parseDateTime(event.getStart()); // converting to ISO 8601
		DateTime end = DateTime.parseDateTime(event.getEnd());
		int tzoffset = -1 * event.getTzoffset(); // browser return is OFFSET(-1*) from utc
		start.setTzShift(tzoffset); 
		end.setTzShift(tzoffset);
		
		// TODO: change for oauth
		CalUpdater updater = new CalUpdater();
//			try {
//				updater.authenTokenLoad();
//			} catch (OAuthException e) {
//				e.printStackTrace();
//			}
		updater.authenSetup("mikew@h5sw.com", "Intern18718");
		
		CalendarEventEntry returnedEntry = 
				updater.addEvent(event.getTitle(), event.getDescription(), start, end);
		
		_ps = _conn.prepareStatement(
				"insert into  " +
				"smotest.zevents(UID, TenantID, TIME_START, TIME_END, TITLE, CONTENT, UserID, TZOFFSET) " +
				"values(?, ?, ?, ?, ?, ?, ?, ?) ");
		// TODO: link user to actual userid
		String user = "someone";
		String eventUID = returnedEntry.getIcalUID().substring(0, returnedEntry.getIcalUID().indexOf('@')) + "";
		_ps.setString(1, eventUID); // UID
		_ps.setString(2, "some tenant"); // TenantID
		_ps.setTimestamp(3, new Timestamp(returnedEntry.getTimes().get(0).getStartTime().getValue())); //StartDateTime
		_ps.setTimestamp(4, new Timestamp(returnedEntry.getTimes().get(0).getEndTime().getValue())); //StartDateTime
		_ps.setString(5, returnedEntry.getTitle().getPlainText());
		_ps.setString(6, returnedEntry.getPlainTextContent());
		_ps.setString(7, user);
		_ps.setInt(8, tzoffset);
		_ps.executeUpdate();
	} // end method
	
	/**
	 * Add a new event to app from HTML (ie: html forms)
	 * @return HTTP response from adding new element to app
	 * @throws IOException 
	 * @throws SQLException 
	 */
	@POST
	@Produces(MediaType.TEXT_HTML)
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	public void postEventHTML(
			@FormParam("title") String title,
			@FormParam("content") String content,
			@FormParam("month") String month,
			@FormParam("day") String day,
			@FormParam("year") String year,
			@FormParam("hour") String hour,
			@FormParam("minute") String minute,
			@FormParam("durationMinutes") int durationMinutes,
			@FormParam("durationHours") int durationHours,
			@Context HttpServletResponse servletResponse
			) throws IOException, SQLException {
		// padding string lengths
		if(month.length() < 2)
			month = "0" + month;
		if(day.length() < 2)
			day = "0" + day;
		if(hour.length() < 2)
			hour = "0" + hour;
		if(minute.length() < 2)
			minute = "0" + minute;
		
		// parsing new DateTime
		DateTime startTime = DateTime.parseDateTime(year + "-" + month + "-" + day + "T"
				+ hour + ":" + minute + ":" + "00");
		
		// push events to google calendar
		CalUpdater updater = new CalUpdater();
//			try {
//				updater.authenTokenLoad();
//			} catch (OAuthException e) {
//				e.printStackTrace();
//			}
		System.out.println("im a new updater");
		updater.authenSetup("mikew@h5sw.com", "Intern18718");
		
		CalendarEventEntry returnedEntry = 
				updater.addEvent(title, content, startTime, durationHours, durationMinutes);
		
		_ps = _conn.prepareStatement(
						"insert into  " +
						"smotest.zevents(UID, TenantID, TIME_START, TIME_END, TITLE, CONTENT, UserID) " +
						"values(?, ?, ?, ?, ?, ?, ?) ");
		// TODO: link user to actual userid
		String user = "someone";
		String eventUID = returnedEntry.getIcalUID().substring(0, returnedEntry.getIcalUID().indexOf('@')) + "";
		_ps.setString(1, eventUID); // UID
		_ps.setString(2, "some tenant"); // TenantID
		_ps.setTimestamp(3, new Timestamp(returnedEntry.getTimes().get(0).getStartTime().getValue())); //StartDateTime
		_ps.setTimestamp(4, new Timestamp(returnedEntry.getTimes().get(0).getEndTime().getValue())); //StartDateTime
		_ps.setString(5, returnedEntry.getTitle().getPlainText());
		_ps.setString(6, returnedEntry.getPlainTextContent());
		_ps.setString(7, user);
		_ps.executeUpdate();
		
		servletResponse.sendRedirect("../test/index.jsp");
	} // end method

	
	/**
	 * Retrieves event in Json, FullCalendar use json natural notation
	 * URL param for start and end time in unix-timestamp
	 * @return Representation of an EventObject in Json form
	 * @throws SQLException 
	 */
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public List<EventObject> getEventJson( // for FullCalendar: query start+end is unix timestamp
			@QueryParam("start") int start,
			@QueryParam("end") int end,
			@QueryParam("tzoffset_browser") int tzoffset) throws SQLException {
		// get params into usable date objects
		// NOTE: datetime from fullcalendar is not UTC (it's parsed to local time); must convert to UTC
		DateTime queryStartDateTime = new DateTime(((long)start) * ((long) 1000) - ((long)tzoffset)*60*1000);
		DateTime endDateTime = new DateTime(((long)end) * ((long) 1000) - ((long)tzoffset)*60*1000);
		
		// query db
		_ps = _conn.prepareStatement("SELECT * " +
				"FROM smotest.zevents " +
				"WHERE TIME_START > ? " +
				"AND TIME_END < ?");
		_ps.setTimestamp(1, new Timestamp(queryStartDateTime.getValue()));
		_ps.setTimestamp(2, new Timestamp(endDateTime.getValue()));
		ResultSet rs = _ps.executeQuery();
		
		// put events on list and return as json natural notation
		List<EventObject> eventList = new LinkedList<EventObject>();
		while(rs.next()) {
			EventObject newEventObject = new EventObject();
			newEventObject.setId(rs.getString(1));
			newEventObject.setStart(rs.getTimestamp(5).getTime(), rs.getInt(9));
			newEventObject.setEnd(rs.getTimestamp(6).getTime(), rs.getInt(9));
			newEventObject.setTitle(rs.getString(7));
			newEventObject.setDescription(rs.getString(8));
			newEventObject.setTzoffset(rs.getInt(9));
			eventList.add(newEventObject);
		}
		
//		System.out.println("i param: start = " + start + " " + queryStartDateTime.toString() + " " + " end = " + end +" " + endDateTime.toString());
		return eventList; // returned as array elements in json
	} // end method
	
	/**
	 * Retrieves event in html form
	 * @return Representation of an event in HTML form
	 */
	@Deprecated
	@GET
	@Produces(MediaType.TEXT_HTML)
	public String getEventHTML() {
		return "<html> HTML: event data in html form goes here </html>";
	} // end method

	/**
	 * Edits event via json
	 * @return 
	 * @throws ServiceException 
	 * @throws IOException 
	 * @throws SQLException 
	 */
	@PUT
	@Consumes(MediaType.APPLICATION_JSON)
	public void putEventJson(String eventJson) throws IOException, ServiceException, SQLException {
		EventChangeObject eventChg = _gson.fromJson(eventJson, EventChangeObject.class); // missing fields
		
		DateTime start = DateTime.parseDateTime(eventChg.getStart()); // converting to ISO 8601
		DateTime end = DateTime.parseDateTime(eventChg.getEnd());
		int tzoffset = -1 * eventChg.getTzoffset(); // browser return is OFFSET(-1*) from utc
		start.setTzShift(tzoffset); 
		end.setTzShift(tzoffset);
		
		long timeDelta = 1000*(24*60*60*eventChg.getDayDelta() + 60*eventChg.getMinuteDelta());
		DateTime prevStart = new DateTime(start.getValue() + timeDelta);
		DateTime prevEnd = new DateTime(end.getValue() + timeDelta);
		
		// chg events in db
		_ps = _conn.prepareStatement("UPDATE smotest.zevents " +
				"SET TIME_START = ?, TIME_END = ?, TZOFFSET = ? " +
				"WHERE UID = ? ");
		_ps.setTimestamp(1, new Timestamp(start.getValue()));
		_ps.setTimestamp(2, new Timestamp(end.getValue()));
		_ps.setInt(3, tzoffset);
		_ps.setString(4, eventChg.getId());
		int result = _ps.executeUpdate();
		
		// chg events on google
		CalUpdater updater = new CalUpdater();
		updater.authenSetup("mikew@h5sw.com", "Intern18718");
		updater.updateEvent(eventChg);
	} // end method
	
	/**
	 * Deletes event json based on path encoded UID
	 * @return 
	 * @throws SQLException 
	 * @throws ServiceException 
	 * @throws IOException 
	 */
	@Path("{uid}")
	@DELETE
	public void deleteEventJson(@PathParam("uid") String passed_uid) throws SQLException, IOException, ServiceException {
		// chg events in db
		_ps = _conn.prepareStatement("SELECT * " +
				"FROM smotest.zevents " +
				"WHERE UID = ? ");
		_ps.setString(1, passed_uid);
		ResultSet rs = _ps.executeQuery();
		EventObject eventObject = new EventObject();
		while(rs.next()) {
			eventObject.setId(rs.getString(1));
			eventObject.setStart(rs.getTimestamp(5).getTime(), rs.getInt(9));
			System.out.println(rs.getTimestamp(5).getTime());
			
			eventObject.setEnd(rs.getTimestamp(6).getTime(), rs.getInt(9));
			eventObject.setTzoffset(rs.getInt(9));
		}
		
		_ps = _conn.prepareStatement("DELETE " +
				"FROM smotest.zevents " +
				"WHERE UID = ? ");
		_ps.setString(1, passed_uid);
		int result = _ps.executeUpdate();
		if(result == 0)
			System.out.println("cannot delete: cannot match uid (may be already deleted)");
		
		// chg events on google
		CalUpdater updater = new CalUpdater();
		updater.authenSetup("mikew@h5sw.com", "Intern18718");
		updater.deleteEvent(eventObject);
	} // end method
} // end class
